package org.groovyflow.validation

import org.groovyflow.util.Rule

abstract class Validator  {
    //The object being validated
    def toValidate

    //Expected usage: each element is of the form [pathToProperty, closure], and
    //if you have more than one element with the same pathToProperty, element n+1
    //is applied only if element n did not result in an error being found.
    //But feel free to pass in a map of closures keyed by the paths to properties--
    //we can handle that to.
    def validations = []

    def errors = [:] //The key is a path, which is a list of path elements
    def beginPath = []
 
    protected Validator(){}
    protected Validator(toValidate){this.toValidate = toValidate}
    abstract def validate()
    
    /**
        Return a map in which the path, which internally is a list of path elements,
        becomes a String.
        TODO  Right now this does not handle anything with more than one path element in it!
    */
    def getErrors(){
        def ret = [:]
        errors.each{k,v ->
            ret[k[0]] = v
        }
        return ret
    }
    
     /**
        Runs through the map or list of validation closures.  When a closure returns
        a Striing, the string gets added to the errors map.
        Note: If there's already an error for a given property, we don't hand the property
        to another validation.  This allows my favorite usage-- a list
        of validations of the form [pathToProperty, closure], in which the same
        property can appear multiple times.  You could, for instance, test once
        for existence of a String, a second time to see if the String can be
        converted to an Integer, and a third time to see if the Integer is in
        some specified format.
    */
    def validateImpl(propertyName, closu){
         def path = beginPath + propertyName 
         if(errors[path] == null){//To give us the functionality mentioned in the note above
             def extraArgs = ["obj" : toValidate, "path" : path]
             def prop = toValidate[propertyName]
             if(closu instanceof List){ //first element decides, second writes the err message
                boolean tf = closu[0](prop ,extraArgs)
                if(tf)
                   errors[path] =  closu[1](prop ,extraArgs)
            }
            else if(closu instanceof Rule){
                 if(closu.pred(prop ,extraArgs))
                     errors[path] =  closu.action(prop ,extraArgs)
            }
            else{    
               def ret = closu(prop ,extraArgs)
               if(ret instanceof String && ret != null && ret.trim().length() > 0)
                 errors[path] = ret
           }
        }
    }

}